package org.springframework.extcache.multilevel;

import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.extcache.AbstractConfiggurableCache;
import org.springframework.extcache.ConfigurableCache;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

public class MultiLevelCache extends AbstractConfiggurableCache {

  private CacheManager      superiorCacheManager;
  private CacheManager      cacheManager;
  private MultiLevelCache   next;
  private MultiLevelCache   previous;

  private ConfigurableCache delegate;
  private boolean           isSuperiorCheckOfCacheManager;
  private boolean           isPutable;

  public MultiLevelCache(ConfigurableCache delegate, boolean isSuperiorCheckOfCacheManager, boolean isPutable) {
    this.delegate = delegate;
    this.cacheManager = delegate.getCacheManager();
    this.isSuperiorCheckOfCacheManager = isSuperiorCheckOfCacheManager;
    this.isPutable = isPutable;
  }

  public boolean isInitialized() {
    return delegate.isInitialized();
  }

  @Override
  public String getActualKey() {
    return delegate.getActualKey();
  }

  protected ConfigurableCache getDelegate() {
    return delegate;
  }

  protected void setDelegate(ConfigurableCache delegate) {
    this.delegate = delegate;
  }

  /**
   * 上级检查，检验通过返回true，否则返回false
   * 
   * @return
   */
  public boolean superiorCheck() {
    if (!isSuperiorCheck()) {// 上级缓存不检查时，始终通过。
      return true;
    }
    MultiLevelCache next = getNext();
    if (next == null) {// 没有下一个节点，直接返回检查通过
      return true;
    }
    CacheManager superior = getSuperiorCacheManager();
    if (superior == null) {
      return true;
    }
    Object value = getValue();
    if (value == null) {
      return false;
    }
    Cache cache = superior.getCache(getName());
    if (cache instanceof ConfigurableCache) {
      ConfigurableCache superiorCache = (ConfigurableCache) cache;
      superiorCache.setActualKey(delegate.getActualKey());
      String superiorVersion = superiorCache.getVersion();
      if (!ObjectUtils.nullSafeEquals(getVersion(), superiorVersion) || superiorVersion == null) {
        this.value = null;
        return false;
      }
    }
    return true;
  }

  public boolean isSuperiorCheck() {
    String isSuperiorCheck = keyValueParser.get(SuperiorCheckHandler.IS_SUPERIOR_CHECK_KEY);
    if (!StringUtils.isEmpty(isSuperiorCheck)) {
      return "true".equalsIgnoreCase(isSuperiorCheck);
    }
    return isSuperiorCheckOfCacheManager;
  }

  @Override
  public Object getValue() {
    return delegate.getValue();
  }

  /**
   * 获取版本号
   * 
   * @return
   */
  public String getVersion() {
    return version;
  }

  /**
   * 设置版本号
   * 
   * @param version
   */
  public void setVersion(String version) {
    delegate.setVersion(version);
    this.version = version;
  }

  /**
   * 如果不存在版本号，分配一个。
   * 
   */
  public void assignVersionIfNotSet() {
    if (StringUtils.isEmpty(this.delegate.getVersion())) {
      this.version = UUID.randomUUID().toString();
      this.delegate.setVersion(this.version);
    }
  }

  /**
   * 获取前结点缓存
   * 
   * @return
   */
  public MultiLevelCache getPrevious() {
    return previous;
  }

  /**
   * 设置前结点缓存
   * 
   * @param previous
   */
  public void setPrevious(MultiLevelCache previous) {
    this.previous = previous;
  }

  /**
   * 获取下一个缓存结点
   * 
   * @return
   */
  public MultiLevelCache getNext() {
    return next;
  }

  /**
   * 设置下一个缓存节点
   * 
   * @param next
   */
  public void setNext(MultiLevelCache next) {
    this.next = next;
  }

  /**
   * 获取上级cacheManager
   * 
   * @return
   */
  public CacheManager getSuperiorCacheManager() {
    return this.superiorCacheManager;
  }

  /**
   * 设置cacheManager
   * 
   * @param cacheManager
   */
  public void setCacheManager(CacheManager cacheManager) {
    this.cacheManager = cacheManager;
  }

  public CacheManager getCacheManager() {
    return cacheManager;
  }

  /**
   * 设置上级cacheManager
   * 
   * @param superiorCacheManager
   */
  public void setSuperiorCacheManager(CacheManager superiorCacheManager) {
    this.superiorCacheManager = superiorCacheManager;
  }

  @Override
  public boolean isLoaded() {
    return delegate.isLoaded();
  }

  @Override
  public String getName() {
    return delegate.getName();
  }

  @Override
  public Object getNativeCache() {
    return delegate.getNativeCache();
  }

  @Override
  public ValueWrapper get(Object key) {
    if (!delegate.isInitialized()) {
      init(key);
    }
    ValueWrapper valueWrapper = delegate.get(getActualKey());
    if (valueWrapper != null) {
      this.version = delegate.getVersion();
      if (!superiorCheck()) {
        valueWrapper = null;
        delegate.evict(key);
      }
    }

    if (valueWrapper == null) {
      MultiLevelCache next = getNext();
      if (next != null) {
        valueWrapper = next.get(key);
        if (valueWrapper != null) {
          next.copyFromLast2Head(key, valueWrapper.get());
        }
      }
    }
    return valueWrapper;
  }

  /**
   * 从上一个有效的缓存结点拷贝到首结点
   * 
   * @author zhangz8@yeah.net
   * @date 2020年1月2日上午9:37:48
   * @param key
   * @param valueWrapper
   * @param next
   */
  private void copyFromLast2Head(Object key, Object value) {
    putByLast(key, value);
  }

  @Override
  public <T> T get(Object key, Class<T> type) {
    return null;
  }

  @Override
  public <T> T get(Object key, Callable<T> valueLoader) {
    return null;
  }

  @Override
  public void put(Object key, Object value) {
    if (!isPutable()) {
      return;
    }

    if (!delegate.isInitialized()) {
      init(key);
    }
    assignVersionIfNotSet();
    delegate.put(key, value);

    MultiLevelCache next = getNext();
    if (next != null) {
      next.setVersion(version);
      next.put(key, value);
    }
  }

  private boolean isPutable() {
    return isPutable;
  }

  /**
   * 从上一个有效结点往首结点put
   * 
   * @author zhangz8@yeah.net
   * @date 2020年1月2日上午9:42:40
   * @param key
   * @param value
   */
  private void putByLast(Object key, Object value) {
    MultiLevelCache previous = getPrevious();
    if (previous != null) {
      previous.setVersion(getVersion());
      previous.delegate.put(key, value);

      previous.putByLast(key, value);
    }
  }

  @Override
  public ValueWrapper putIfAbsent(Object key, Object value) {
    return null;
  }

  @Override
  public void evict(Object key) {
    if (!delegate.isInitialized()) {
      init(key);
    }

    delegate.evict(actualKey);

    MultiLevelCache next = getNext();
    if (next != null) {
      next.evict(key);
    }
  }

  @Override
  public void clear() {
    delegate.clear();
    MultiLevelCache next = getNext();
    if (next != null) {
      next.clear();
    }
  }

  @Override
  protected boolean init(Object objKey) {
    if (!isInitialized()) {
      SuperiorCheckHandler handler = new SuperiorCheckHandler();
      String rawKey = objKey.toString();
      boolean isInit = super.init(rawKey);
      if (isInit) {
        delegate.setIsInitialized(isInit);
      }
      String actualKey = handler.clean(this.actualKey);
      setActualKey(actualKey);
      String[] keyValue = handler.handle(objKey.toString());
      if (keyValue != null) {
        keyValueParser.put(keyValue[0], keyValue[1]);
      }
      delegate.setKeyValueParser(keyValueParser);
      return isInit;
    }
    return true;
  }

  public void setActualKey(String actualKey) {
    super.setActualKey(actualKey);
    delegate.setActualKey(actualKey);
  }

  class SuperiorCheckHandler implements KeyValueHandler {
    static final String IS_SUPERIOR_CHECK_KEY = "isSuperiorCheck";
    static final String IS_SUPERIOR_CHECK_REG = "\\s*,?\\s*" + IS_SUPERIOR_CHECK_KEY + "\\s*=\\s*\\w+\\s*,?";

    @Override
    public String[] handle(String rawKey) {
      if (rawKey.indexOf(IS_SUPERIOR_CHECK_KEY) > -1) {
        String[] keyValue = new String[2];
        keyValue[0] = IS_SUPERIOR_CHECK_KEY;
        Matcher matcher = Pattern.compile(IS_SUPERIOR_CHECK_REG, Pattern.CASE_INSENSITIVE).matcher(rawKey);
        if (matcher.find()) {
          String value = matcher.group();
          value = value.replaceAll("\\s*" + IS_SUPERIOR_CHECK_KEY + "\\s*=\\s*", "");
          value = replaceSpace(value);
          keyValue[1] = value;
          return keyValue;
        }
      }
      return null;
    }

    @Override
    public String clean(String rawKey) {
      if (handle(rawKey) != null) {
        String clear = new String(rawKey);
        return clear.replaceAll(IS_SUPERIOR_CHECK_REG, "");
      }
      return rawKey;
    }
  }

}
